package com.chen.authority.common.mapper;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.*;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

/**
 * 自定义mapper，扩展mapper方法
 *
 * @param <M> mapper 泛型
 * @param <T> table 泛型
 * @param <V> vo 泛型
 * @author chengy
 * @date 2022/11/10
 */
@SuppressWarnings("unchecked")
public interface BaseMapperPlus<M, T, V> extends BaseMapper<T> {

    Log log = LogFactory.getLog(BaseMapperPlus.class);

    /**
     * 默认批次提交数量
     */
    int DEFAULT_BATCH_SIZE = 1000;

    default Class<M> currentMapperClass() {
        return (Class<M>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 0);
    }

    default Class<T> currentModelClass() {
        return (Class<T>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 1);
    }

    default Class<V> currentVoClass() {
        return (Class<V>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 2);
    }

    /**
     * 批量插入
     *
     * @param entityList 实体集合
     * @return 是否成功
     */
    default boolean insertBatch(Collection<T> entityList) {
        return insertBatch(entityList, DEFAULT_BATCH_SIZE);
    }

    /**
     * 批量更新
     *
     * @param entityList 实体集合
     * @return 是否成功
     */
    default boolean updateBatch(Collection<T> entityList) {
        return updateBatch(entityList, DEFAULT_BATCH_SIZE);
    }

    /**
     * 批量插入或更新
     */
    default boolean insertOrUpdateBatch(Collection<T> entityList) {
        return insertOrUpdateBatch(entityList, DEFAULT_BATCH_SIZE);
    }

    /**
     * 批量插入（包含限制条数）
     *
     * @param entityList 实体集合
     * @param batchSize  批次数量
     * @return 是否成功
     */
    default boolean insertBatch(Collection<T> entityList, int batchSize) {
        String sqlStatement = getSqlStatement(SqlMethod.INSERT_ONE);
        return SqlHelper.executeBatch(this.currentModelClass(), log, entityList, batchSize,
                (sqlSession, entity) -> sqlSession.insert(sqlStatement, entity));
    }

    /**
     * 批量更新（包含限制条数）
     *
     * @param entityList 实体集合
     * @param batchSize  批次数量
     * @return 是否成功
     */
    default boolean updateBatch(Collection<T> entityList, int batchSize) {
        String sqlStatement = getSqlStatement(SqlMethod.UPDATE_BY_ID);
        return SqlHelper.executeBatch(this.currentModelClass(), log, entityList, batchSize,
                (sqlSession, entity) -> {
                    MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
                    param.put(Constants.ENTITY, entity);
                    sqlSession.update(sqlStatement, param);
                });
    }


    /**
     * 批量插入或更新（包含限制条数）
     *
     * @param entityList 实体集合
     * @param batchSize  批次数量
     * @return 是否成功
     */
    default boolean insertOrUpdateBatch(Collection<T> entityList, int batchSize) {
        TableInfo tableInfo = TableInfoHelper.getTableInfo(currentMapperClass());
        Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!");
        String keyProperty = tableInfo.getKeyProperty();
        Assert.notEmpty(keyProperty, "error: can not execute. because can not find column for id from entity!");
        return SqlHelper.saveOrUpdateBatch(currentModelClass(), currentMapperClass(), this.log, entityList,
                batchSize, (sqlSession, entity) -> {
                    Object idVal = tableInfo.getPropertyValue(entity, keyProperty);
                    return StringUtils.checkValNull(idVal)
                            || CollectionUtils.isEmpty(sqlSession.selectList(getSqlStatement(SqlMethod.SELECT_BY_ID), entity));
                }, (sqlSession, entity) -> {
                    MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
                    param.put(Constants.ENTITY, entity);
                    sqlSession.update(getSqlStatement(SqlMethod.UPDATE_BY_ID), param);
                });
    }

    /**
     * 插入或更新
     *
     * @param entity 实体
     * @return 是否成功
     */
    default boolean insertOrUpdate(T entity) {
        if (Objects.nonNull(entity)) {
            TableInfo tableInfo = TableInfoHelper.getTableInfo(currentModelClass());
            Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!");
            String keyProperty = tableInfo.getKeyProperty();
            Assert.notEmpty(keyProperty, "error: can not execute. because can not find column for id from entity!");
            Object idVal = tableInfo.getPropertyValue(entity, tableInfo.getKeyProperty());
            return StringUtils.checkValNull(idVal) || Objects.isNull(selectById((Serializable) idVal)) ?
                    insert(entity) > 0 : updateById(entity) > 0;
        }
        return false;
    }

    /**
     * 根据id查询Vo
     *
     * @param id 主键
     * @return Vo
     */
    default V selectVoById(Serializable id) {
        T obj = this.selectById(id);
        if (ObjectUtil.isNull(obj)) {
            return null;
        }
        return BeanUtil.toBean(obj, currentVoClass());
    }

    /**
     * 根据ids批量查询Vo
     *
     * @param idList id列表
     * @return Vo列表
     */
    default List<V> selectVoBatchIds(Collection<? extends Serializable> idList) {
        List<T> list = this.selectBatchIds(idList);
        if (CollUtil.isEmpty(list)) {
            return CollUtil.newArrayList();
        }
        return BeanUtil.copyToList(list, currentVoClass());
    }

    /**
     * 根据 entity 条件，查询一条记录
     *
     * @param wrapper 查询条件
     * @return Vo列表
     */
    default V selectVoOne(Wrapper<T> wrapper) {
        T obj = this.selectOne(wrapper);
        if (ObjectUtil.isNull(obj)) {
            return null;
        }
        return BeanUtil.toBean(obj, currentVoClass());
    }

    /**
     * 根据 entity 条件，查询全部记录
     *
     * @param wrapper 查询条件
     * @return Vo列表
     */
    default List<V> selectVoList(Wrapper<T> wrapper) {
        List<T> list = this.selectList(wrapper);
        if (CollUtil.isEmpty(list)) {
            return CollUtil.newArrayList();
        }
        return BeanUtil.copyToList(list, currentVoClass());
    }

    /**
     * 分页查询Vo
     *
     * @param wrapper 查询条件
     * @return voPage
     */
    default Page<V> selectVoPage(Page<T> page, Wrapper<T> wrapper) {
        Page<T> doPage = this.selectPage(page, wrapper);
        Page<V> voPage = new Page<>(doPage.getCurrent(), doPage.getSize(), doPage.getTotal());
        if (CollUtil.isEmpty(doPage.getRecords())) {
            return voPage;
        }
        voPage.setRecords(BeanUtil.copyToList(doPage.getRecords(), currentVoClass()));
        return voPage;
    }

    /**
     * 获取mapperStatementId
     *
     * @param sqlMethod 方法名
     * @return 命名id
     */
    default String getSqlStatement(SqlMethod sqlMethod) {
        return SqlHelper.getSqlStatement(currentMapperClass(), sqlMethod);
    }

}
